/**
 * @file dwt.c
 * @author chushang (email:chushangcs@163.com)
 * @brief 2025-04-19: 添加dwt使能，基础延时等基础功能
 * @version 0.1
 * @date 2025-04-19
 *
 * @copyright Copyright (c) 2025
 *
 */

#include "dwt.h"

#define CYCLE_PER_OVERFLOW (0x100000000ULL) // 64位无符号常量,用来替代(UINT32_MAX + 1)

static uint32_t cpu_freq_mhz; // cpu的频率，单位MHz;
static uint32_t cpu_freq_khz; // cpu的频率，单位KHz;
static uint32_t cpu_freq_hz;  // cpu的频率，单位Hz;

static uint32_t dwt_last_cnt; // 上一次dwt的计数数值

static uint32_t dwt_timeout_cnt = 0; // dwt溢满次数

SysTime sys_time; // 系统运行的时间

/**
 * @brief 更新dwt_last_cnt，并且统计DWT溢出次数
 *
 */
static void DWT_UpdateCNT(void)
{
    volatile static uint8_t locker = 0; // 锁
    if (!locker)
    {
        locker = 1; // 上锁
        volatile uint32_t curr_cnt = DWT->CYCCNT;
        if (curr_cnt <= dwt_last_cnt)
        {
            dwt_timeout_cnt++; // 溢满一次
        }
        dwt_last_cnt = DWT->CYCCNT; // 更新
        DWT_UpdateSysTime();
        locker = 0; // 解锁
    }
}

/**
 * @brief 初始化DWT内核外设
 *
 * @param _freq_mhz [in] cpu运行频率，单位MHz
 */
void DWT_Init(uint32_t _freq_mhz)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 使能DEMCR
    DWT->CYCCNT = 0;                                // 计数清零
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;            // 使能DWT

    cpu_freq_mhz = _freq_mhz;
    cpu_freq_khz = _freq_mhz * 1000;
    cpu_freq_hz = _freq_mhz * 1000000;

    DWT_UpdateCNT();
}

/**
 * @brief 更新系统运行时间
 *
 */
void DWT_UpdateSysTime(void)
{
    volatile uint32_t curr_cnt = DWT->CYCCNT;
    // 这里请不要直接使用(UINT32_MAX + 1)，虽然UINT32_MAX为宏定义0xffffffffUL，但是让它+1的话会溢出变为0
    // 需要将UINT32_MAX强转为(uint64_t)或更大的数据类型
    uint64_t total_cycles = (uint64_t)dwt_timeout_cnt * UINT32_MAX + curr_cnt;
    // uint64_t total_cycles = (uint64_t)dwt_timeout_cnt * ((uint64_t)UINT32_MAX + 1) + curr_cnt; // 注意会溢出,所以要强转
    // uint64_t total_cycles = (uint64_t)dwt_timeout_cnt * CYCLE_PER_OVERFLOW + curr_cnt; // 和上一条语句二选一
    uint64_t total_us = total_cycles / cpu_freq_mhz; // 转换为微秒

    sys_time.s = total_us / 1000000;
    uint32_t remaining_us = total_us % 1000000;
    sys_time.ms = remaining_us / 1000;
    sys_time.us = remaining_us % 1000;
}

/**
 * @brief 读取系统时间戳，单位s
 *
 * @return 系统运行的时间，单位s
 */
float DWT_GetTimeStamp_s(void)
{
    DWT_UpdateCNT();
    DWT_UpdateSysTime();
    float ret;
    ret = sys_time.s + sys_time.ms * 0.001f + sys_time.us * 0.000001f;
    return ret;
}

/**
 * @brief 读取系统时间戳，单位ms
 *
 * @return 系统运行的时间，单位ms
 */
float DWT_GetTimeStamp_ms(void)
{
    DWT_UpdateCNT();
    DWT_UpdateSysTime();
    float ret;
    ret = sys_time.s * 1000 + sys_time.ms + sys_time.us * 0.001f;
    return ret;
}

/**
 * @brief 读取系统时间戳，单位us
 *
 * @return 系统运行的时间，单位us
 */
float DWT_GetTimeStamp_us(void)
{
    DWT_UpdateCNT();
    DWT_UpdateSysTime();
    float ret;
    ret = sys_time.s * 1000000 + sys_time.ms * 1000 + sys_time.us;
    return ret;
}

/**
 * @brief 延时指定时间
 *
 * @param time_s [in] 延时的时长，单位s
 */
void DWT_Delay_s(float time_s)
{
    DWT_Delay_us(time_s * 1000000);
}

/**
 * @brief 延时指定时间
 *
 * @param time_s [in] 延时的时长，单位ms
 */
void DWT_Delay_ms(float time_ms)
{
    DWT_Delay_us(time_ms * 1000);
}

/**
 * @brief 延时指定时间
 *
 * @param time_s [in] 延时的时长，单位us
 */
void DWT_Delay_us(float time_us)
{
    uint32_t time_start = DWT->CYCCNT;
    while ((DWT->CYCCNT - time_start) < time_us * cpu_freq_mhz)
        ;
}
